﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionHelper
{
    //IDataReader、DataTable、DataRow转Entity扩展
    public static partial class ExtensionHelper
    {
        #region IDataReader
        /// <summary>
        /// 转换成List
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="reader"></param>
        /// <param name="isClose"></param>
        /// <returns></returns>
        public static List<TResult> ToList<TResult>(this IDataReader reader, bool isClose = true) where TResult : class, new()
        {
            List<TResult> list = new List<TResult>();
            if (reader == null) return list;

            EmitEntityBuilder<TResult>.DynamicMethodDelegate<IDataRecord> handler = EmitEntityBuilder<TResult>.CreateHandler(reader);
            while (reader.Read())
            {
                list.Add(handler(reader));
            }
            if (isClose) { reader.Close(); reader.Dispose(); reader = null; }
            return list;
        }

        #endregion

        #region DataTable
        /// <summary>
        /// 转换成List
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>         
        public static List<TResult> ToList<TResult>(this DataTable dt) where TResult : class, new()
        {
            List<TResult> list = new List<TResult>();
            if (dt == null) return list;
            //构造转换方法的委托
            EmitEntityBuilder<TResult>.DynamicMethodDelegate<DataRow> handler = EmitEntityBuilder<TResult>.CreateHandler(dt.Rows[0]);
            foreach (DataRow info in dt.Rows)
            {
                list.Add(handler(info));
            }
            dt.Dispose(); dt = null;
            return list;
        }

        /// <summary>
        /// 转换成T
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>         
        public static TResult ToList<TResult>(this DataRow dr) where TResult : class, new()
        {
            if (dr == null) return default(TResult);

            //构造转换方法的委托
            EmitEntityBuilder<TResult>.DynamicMethodDelegate<DataRow> handler = EmitEntityBuilder<TResult>.CreateHandler(dr);
            return handler(dr);
        }
        #endregion

        /// <summary>
        /// 转换成DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="col"></param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(this IEnumerable<T> col)
        {
            if (!col.IsEmpty())
            {
                var ps = typeof(T).GetProperties();
                Type targetType;
                List<DataColumn> cols = new List<DataColumn>();
                foreach (var p in ps)
                {
                    targetType = p.PropertyType;
                    if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                    {
                        targetType = targetType.GetGenericArguments()[0];
                    }
                    cols.Add(new DataColumn(p.Name, targetType));
                }
                DataTable dt = new DataTable();
                dt.Columns.AddRange(cols.ToArray());

                List<object> objs = null;
                foreach (T model in col)
                {
                    objs = new List<object>();
                    objs.AddRange(ps.Select(p => p.GetValue(model, null)));
                    dt.Rows.Add(objs.ToArray());
                }
                return dt;
            }
            throw new ArgumentNullException();
        }

        /// <summary>
        /// 转换成DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="col"></param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(this T obj)
        {
            if (obj != null)
            {
                Type type = typeof(T);
                var ps = type.GetProperties();
                Type targetType;
                NullableConverter nullableConvert;
                List<DataColumn> cols = new List<DataColumn>();
                foreach (var p in ps)
                {
                    if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                    {
                        nullableConvert = new NullableConverter(p.PropertyType);
                        targetType = nullableConvert.UnderlyingType;
                        cols.Add(new DataColumn(p.Name, targetType));
                    }
                    else
                    {
                        cols.Add(new DataColumn(p.Name, p.PropertyType));
                    }
                }
                DataTable dt = new DataTable();
                dt.Columns.AddRange(cols.ToArray());

                List<object> objs = new List<object>();
                objs.AddRange(ps.Select(p => p.GetValue(obj, null)));
                dt.Rows.Add(objs.ToArray());

                return dt;
            }
            throw new ArgumentNullException();
        }

        /// <summary>
        /// DataRow和IDataReader转换实体类
        /// </summary>
        /// <typeparam name="ItemType"></typeparam>
        private class EmitEntityBuilder<ItemType>
        {
            #region 不可改变的参数
            private static readonly MethodInfo getRow =
                    typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });
            private static readonly MethodInfo isDBNullRow =
                    typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });

            private static readonly MethodInfo getRecord =
                    typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
            private static readonly MethodInfo isDBNullRecord =
                    typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
            #endregion

            // 自定义转换实体委托
            public delegate ItemType DynamicMethodDelegate<TParam>(TParam paramObjs);

            private EmitEntityBuilder() { }
            /// <summary>
            /// 创建委托
            /// </summary>
            /// <param name="row"></param>
            /// <returns></returns>
            public static DynamicMethodDelegate<DataRow> CreateHandler(DataRow row)
            {
                System.Type itemType = typeof(ItemType);
                DynamicMethod method = new DynamicMethod("DynamicCreateEntity",
                    itemType,
                    new Type[] { typeof(DataRow) },
                    itemType, true);

                ILGenerator generator = method.GetILGenerator();
                LocalBuilder result = generator.DeclareLocal(itemType);
                generator.Emit(OpCodes.Newobj, itemType.GetConstructor(Type.EmptyTypes));
                generator.Emit(OpCodes.Stloc, result);

                for (int i = 0; i < row.ItemArray.Length; i++)
                {
                    PropertyInfo propertyInfo
                        = itemType.GetProperty(row.Table.Columns[i].ColumnName);
                    Label endIfLabel = generator.DefineLabel();
                    if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                    {
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, isDBNullRow);
                        generator.Emit(OpCodes.Brtrue, endIfLabel);
                        generator.Emit(OpCodes.Ldloc, result);
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, getRow);
                        generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                        generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                        generator.MarkLabel(endIfLabel);
                    }
                }
                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ret);

                DynamicMethodDelegate<DataRow> handler
                       = (DynamicMethodDelegate<DataRow>)method.CreateDelegate(typeof(DynamicMethodDelegate<DataRow>));
                return handler;
            }
            public static DynamicMethodDelegate<IDataRecord> CreateHandler(IDataRecord dataRecord)
            {
                System.Type itemType = typeof(ItemType);
                DynamicMethod method = new DynamicMethod("DynamicCreateEntity",
                        itemType,
                        new Type[] { typeof(IDataRecord) },
                        itemType, true);
                ILGenerator generator = method.GetILGenerator();
                LocalBuilder result = generator.DeclareLocal(itemType);
                generator.Emit(OpCodes.Newobj, itemType.GetConstructor(Type.EmptyTypes));
                generator.Emit(OpCodes.Stloc, result);
                for (int i = 0; i < dataRecord.FieldCount; i++)
                {
                    PropertyInfo propertyInfo
                        = itemType.GetProperty(dataRecord.GetName(i));
                    Label endIfLabel = generator.DefineLabel();
                    if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                    {
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, isDBNullRecord);
                        generator.Emit(OpCodes.Brtrue, endIfLabel);
                        generator.Emit(OpCodes.Ldloc, result);
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, getRecord);
                        generator.Emit(OpCodes.Unbox_Any, dataRecord.GetFieldType(i));
                        generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                        generator.MarkLabel(endIfLabel);
                    }
                }
                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ret);

                DynamicMethodDelegate<IDataRecord> handler
                       = (DynamicMethodDelegate<IDataRecord>)method.CreateDelegate(typeof(DynamicMethodDelegate<IDataRecord>));
                return handler;
            }
        }
    }

}
